/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef UTILS_NAPI_HANDLER_H
#define UTILS_NAPI_HANDLER_H
#include "native_common.h"
#include "ohos_log.h"
#include <functional>
#include <napi/native_api.h>
#include <stdint.h>
#include <string>


const int32_t NAPI_MAX_ARGV = 8;
const int32_t INDEX_0 = 0;
const int32_t INDEX_1 = 1;
const int32_t INDEX_2 = 2;
const int32_t INDEX_3 = 3;
const int32_t INDEX_4 = 4;
const int32_t INDEX_5 = 5;
const int32_t INDEX_6 = 6;
const int32_t INDEX_7 = 7;
const int32_t PARAM_COUNT_1 = 1;
const int32_t PARAM_COUNT_2 = 2;
const int32_t PARAM_COUNT_3 = 3;
const int32_t PARAM_COUNT_4 = 4;
const int32_t PARAM_COUNT_5 = 5;
const int32_t PARAM_COUNT_6 = 6;
const int32_t PARAM_COUNT_7 = 7;
const int32_t PARAM_COUNT_8 = 8;

#define NAPI_CALL_HANDLE(env, theCall, retVal)                                                                         \
    do {                                                                                                               \
        napi_status stat;                                                                                              \
        if ((stat = (theCall)) != napi_ok) {                                                                           \
            LOGE("fail to call %s, status: %d", #theCall, stat);                                                       \
            return retVal;                                                                                             \
        }                                                                                                              \
    } while (0)

class NapiHandler {
public:
    NapiHandler(napi_env &env, napi_callback_info &info, int argn);
    virtual ~NapiHandler();
    napi_status ParseArgs(int index, const std::function<napi_status(const napi_value &)> &doArgs);

    template <typename T> T ParseArg(const napi_value &arg);
    template <typename T> T ParseArgAs(int index) {
        T result;
        NAPI_ASSERT_BASE(env_, index < argc_, "Index out of range", result);
        result = ParseArg<T>(argv_[index]);
        return result;
    }

    template <typename T> T ParseArgAs(int index, const std::function<T(const napi_value &)> &doArgs) {
        T result;
        NAPI_ASSERT_BASE(env_, index < argc_, "Index out of range", result);
        result = doArgs(argv_[index]);
        return result;
    }

    napi_value GetVoidValue();

    template <class T> napi_value GetNapiValue(const T &v);

    template <class T> void SetObjectProp(napi_value &obj, const std::string &prop, const T &value) {
        napi_value result = GetNapiValue<T>(value);
        if (result == nullptr) {
            return;
        }
        napi_set_named_property(env_, obj, prop.c_str(), result);
    }

    template <class T>
    T GetObjectProp(int index, const std::string &prop, const std::function<T(const napi_value &)> &doArgs) {
        napi_value result;
        T t;
        NAPI_ASSERT_BASE(env_, index < argc_, "Index out of range", result);
        NAPI_CALL_HANDLE(env_, napi_get_named_property(env_, argv_[index], prop.c_str(), &result), t);
        return doArgs(result);
    }

    template <class T> T GetObjectProp(int index, const std::string &prop) {
        T t;
        napi_value result;
        NAPI_ASSERT_BASE(env_, index < argc_, "Index out of range", t);
        NAPI_CALL_HANDLE(env_, napi_get_named_property(env_, argv_[index], prop.c_str(), &result), t);
        return ParseArg<T>(result);
    }

    template <class T> T CallMethod(const napi_ref &funcRef, const std::vector<napi_value> &args = {}) {
        T v;
        napi_value jsFunc;
        NAPI_CALL_HANDLE(env_, napi_get_reference_value(env_, funcRef, &jsFunc), v);
        napi_value result;
        NAPI_CALL_HANDLE(env_, napi_call_function(env_, nullptr, jsFunc, args.size(), args.data(), &result), v);
        v = ParseArg<T>(result);
        return v;
    }

    napi_value CreateArray(int len, const std::function<void(int, napi_value &)> &doArray);

private:
    napi_env env_;
    size_t argc_ = 0;
    napi_value *argv_ = nullptr;
    napi_value thisArg_ = nullptr;
    void *data_ = nullptr;
};
#endif // YYEVA_NAPI_HANDLER_H
